iT邦幫忙

2024 iThome 鐵人賽

DAY 10
0
Python

時空序列分析-關鍵籌碼分析系列 第 10

爬蟲拿資料? 來學習怎麼畫股價K線圖! Step(2/2): 自己爬股價

  • 分享至 

  • xImage
  •  

前天 用 FinMind 可以拿到股價資料,
昨天參考到的文章內也有教學如何使用爬蟲來抓取資料,

除了用FinMind,今天我們來研究一下如何用爬蟲抓股價好了~

這篇文章的作者08.爬股市每日價、量用下列這兩段網址分別可以抓到上市、上櫃公司的股價。

台股上市公司-抓取網址(證交所資料)

http://www.twse.com.tw/exchangeReport/STOCK_DAY?response=json&date=20230808&stockNo=2330

有爬蟲經驗的朋友就知道,

有了這個網址就很好辦! 我們觀察裡面的結構並且拆解開來解讀,再用python跑迴圈

response = json: 用json的格式回應資料。
date = 20230808: 日期設定在2023年8月8日(其實沒啥用),因為它會幫你整個月份抓出來
stockNo = 2330:抓取股票代號為2330的這檔上市股票的資料 (2330=台積電)。

可以單純用個瀏覽器打開,看看資料大概會長什麼樣子(如下圖)
https://ithelp.ithome.com.tw/upload/images/20240809/201683225ZNoNWYyFk.png

抓下來的資料裡面都有寫到欄位是甚麼

fields":["日期","成交股數","成交金額","開盤價","最高價","最低價","收盤價","漲跌價差","成交筆數"]

台股上櫃公司-抓取網址(櫃台買賣中心資料)

http://www.tpex.org.tw/web/stock/aftertrading/daily_trading_info/st43_result.php?d=112/08&stkno=6223

會發現,這串跟上面好像長得不一樣,用不同的名稱來抓同種類型的東西

d=112/08: 日期設定在2023年8月,整個月份非常直接了當。
stkno = 6223: 抓取股票代號為6223的這檔上櫃股票的資料,(6223=旺矽)。

可以看見,跟上市的格式、內容不太一樣,但欄位資料的順序都一樣(如下圖)
https://ithelp.ithome.com.tw/upload/images/20240809/20168322Fi0vUMAlmz.png

上櫃這邊就沒寫,不過欄位一樣是

fields":["日期","成交股數","成交金額","開盤價","最高價","最低價","收盤價","漲跌價差","成交筆數"]

但實際我們只要裡面的重要欄位就好,裡面還有很多標點符號、斜線、notes甚麼的,

其他內容都摒除掉。

因為我沒有打算建資料庫,就參考他抓資料和處理資料的方式就好。

(我才不會說我看不懂他的東西 :D )

像是這邊把日期從民國年,+1911年轉成西元年,ex.「112/01/01」->「2023/01/01」。
還有把「成交股數」、「成交金額」和「成交筆數」這三個欄位的逗號刪去。
把「成交股數」和「成交金額」的數值除以1000。
最後把「成交股數」列的名稱改為「成交張數」。

import re
data['日期']=data['日期'].apply(lambda x: re.sub('(\d+)(/\d+/\d+)',lambda y: str(int(y.group(1))+1911)+y.group(2),x))
data[['成交股數','成交金額','成交筆數']]=data[['成交股數','成交金額','成交筆數']].applymap(lambda x:x.replace(',',''))
data.iloc[:,1:]=data.iloc[:,1:].applymap(float)
data[['成交股數','成交金額']]=data[['成交股數','成交金額']]/1000

data = data.rename(columns={'成交股數': '成交張數'})

還記得第三天的時候做的籌碼分析-實作簡單的籌碼分析,資料從哪裡收集? 有資料集還是要自已爬?
裡面的一些程式碼也會用到~

這邊以自己的寫法從上市股票開始爬,然後做成K線圖!

重點mplfinance是 matplotlib 的一個擴展工具,專門用來繪製金融圖表
如果沒有安裝的話要記得
pip install mplfinance

首先引入需要的套件庫

import pandas as pd
import requests
import re
import time
import mplfinance as mpf
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
from datetime import datetime

一樣把要畫的圖表字體改成中文的標楷體除非用英文不然不加這段會顯示空格

# 設定字體
zhfont = fm.FontProperties(fname=r'C:\Windows\Fonts\kaiu.ttf')  # 使用原始字串
plt.rcParams['font.family'] = zhfont.get_name()

接下來定義一個根據上面下載"上市公司"資料的function

# 定義下載資料的函數
def fetch_data(date, stock_no):
    url = f'http://www.twse.com.tw/exchangeReport/STOCK_DAY?response=json&date={date}&stockNo={stock_no}'
    max_retries = 5
    for i in range(max_retries):
        try:
            response = requests.get(url)
            response.raise_for_status()  # 如果請求失敗,則引發異常
            return response.json()
        except requests.exceptions.RequestException as e:
            print(f'Error fetching data for {date}: {e}. Retry {i + 1}/{max_retries}')
            time.sleep(3)
    raise Exception(f'Failed to fetch data for {date} after {max_retries} retries')

觀察過要下載的資料後,找方法和debug了好久,寫成這樣
這邊設定要抓的股票代號,這邊用台積電來舉例,stock_no='2330'

# 定義下載和合併資料的函數
def download_stock_data(start_year, start_month, end_year, end_month, stock_no='2330'):
    data_frames = []
    for year in range(start_year, end_year + 1):
        start_m = start_month if year == start_year else 1
        end_m = end_month if year == end_year else 12
        for month in range(start_m, end_m + 1):
            date_str = f'{year}{month:02d}01'
            try:
                json_data = fetch_data(date_str, stock_no)
                columns = ['日期', '成交股數', '成交金額', '開盤價', '最高價', '最低價', '收盤價', '漲跌價差', '成交筆數']
                df = pd.DataFrame(json_data['data'], columns=columns)
                data_frames.append(df)
            except Exception as e:
                print(f'Error fetching data for {date_str}: {e}')
            # 跳過未來的月份
            current_date = datetime.now()
            if year == current_date.year and month >= current_date.month:
                break
    all_data = pd.concat(data_frames, ignore_index=True)
    return all_data

開始把日期轉換和資料前處理的部分寫出來

# 日期轉換和資料清洗
def clean_data(all_data):
    all_data['日期'] = all_data['日期'].apply(lambda x: re.sub(r'(\d+)(/\d+/\d+)', lambda y: str(int(y.group(1)) + 1911) + y.group(2), x))
    all_data[['成交股數', '成交金額', '成交筆數']] = all_data[['成交股數', '成交金額', '成交筆數']].replace(',', '', regex=True)

    # 移除無法轉換為數字的異常值
    def to_float(x):
        try:
            return float(x)
        except ValueError:
            return float('nan')

    all_data.iloc[:, 1:] = all_data.iloc[:, 1:].applymap(to_float)

    # 轉換成交股數和成交金額的單位
    all_data[['成交股數', '成交金額']] = all_data[['成交股數', '成交金額']] / 1000
    all_data = all_data.rename(columns={'成交股數': '成交張數'})

    # 移除包含 NaN 的行
    all_data = all_data.dropna()

    # 轉換日期為日期格式
    all_data['日期'] = pd.to_datetime(all_data['日期'])

    # 重命名列以符合 mplfinance 的要求
    all_data = all_data.rename(columns={
        '開盤價': 'Open',
        '最高價': 'High',
        '最低價': 'Low',
        '收盤價': 'Close',
        '成交張數': 'Volume'
    })

    # 確保所有列都轉換為浮點數類型
    all_data['Open'] = all_data['Open'].astype(float)
    all_data['High'] = all_data['High'].astype(float)
    all_data['Low'] = all_data['Low'].astype(float)
    all_data['Close'] = all_data['Close'].astype(float)
    all_data['Volume'] = all_data['Volume'].astype(float)

    # 設置日期為索引
    all_data.set_index('日期', inplace=True)

    return all_data

這邊讓資料可以調整,因為有一些bug,把資料清理過一遍
設定開始時間今年4月~6月

# 主程序
start_year = 2024
start_month = 4
end_year = 2024
end_month = 6

all_data = download_stock_data(start_year, start_month, end_year, end_month)
cleaned_data = clean_data(all_data)
print(cleaned_data)

做好資料處理,把資料印出來看看!

all_data

Volume 成交金額 Open High Low Close 漲跌價差
日期
2024-04-01 22348.250 17301753.062 783.0 783.0 769.0 770.0 -9.0
2024-04-02 42219.075 33230356.267 784.0 790.0 783.0 790.0 20.0
2024-04-03 32909.892 25719094.412 783.0 785.0 778.0 780.0 -10.0
2024-04-08 40567.580 31925988.285 789.0 792.0 783.0 783.0 3.0
...

如果想要的話,可以把他保存起來變成xlsx 或 csv (改成 to_csv)

# 將結果保存到新的Excel檔案
results_df = pd.DataFrame(cleaned_data, columns=['日期', 'Volume', '成交金額', 'Open', 'High', 'Low', 'Close', '漲跌價差'])
results_df.to_excel('stock_2330_data.xlsx', index=False)

資料順利拿到,終於可以開始畫圖了!

# 繪製 K 線圖
def plot_data(all_data, title):
    fig, ax = plt.subplots()

    # mplfinance.plot 函數不接受 fontproperties 這個參數。
    # 相反,我們可以在設置標題和標籤時手動指定字體屬性。
    mpf.plot(all_data, type='candle', style='charles', ax=ax, warn_too_much_data=len(all_data) + 1)
    ax.set_title(title, fontproperties=zhfont)
    ax.set_ylabel('價格', fontproperties=zhfont)
    plt.show()
    
# 這邊改一下圖表上的名稱為 台積電
plot_data(cleaned_data, f'台積電 {start_year}/{start_month} - {end_year}/{end_month} 每日股票交易價格和收盤情況')

但是畫出來有發現嗎...好像哪裡怪怪的? (如下圖)
https://ithelp.ithome.com.tw/upload/images/20240809/20168322erFVvh6UVM.png

怎麼是 綠漲紅跌..? 沒錯,這個是美國線

所以來稍微自己改一下,改成台股的紅漲綠跌

創建一個自定義的顏色樣式字典,傳給mplfinance.plot函數

def plot_data(all_data, title):
    fig, ax = plt.subplots()

    # 自定義樣式
    custom_style = mpf.make_mpf_style(
        base_mpf_style='charles',
        marketcolors={
            'candle': {'up': 'red', 'down': 'green'},
            'edge': {'up': 'red', 'down': 'green'},
            'wick': {'up': 'red', 'down': 'green'},
            'ohlc': {'up': 'red', 'down': 'green'},
            'volume': {'up': 'red', 'down': 'green'},
            'alpha': 1.0
        }
    )

    # 繪圖
    mpf.plot(all_data, type='candle', style=custom_style, ax=ax, warn_too_much_data=len(all_data) + 1)
    ax.set_title(title, fontproperties=zhfont)
    ax.set_ylabel('價格', fontproperties=zhfont)
    plt.show()

就可以自己畫出美美的圖了,滿滿的成就感! /images/emoticon/emoticon24.gif
https://ithelp.ithome.com.tw/upload/images/20240809/20168322UtOJsVJfri.png

這個寫法,缺點是要記好是上市,還是上櫃,不然搞錯市場別,一定抓不到。


每日記錄:
禮拜日應該放假的說,
明天加碼來寫上櫃,還有合併一起判斷。

默默地完成了1/3的發文了 (覺得自己棒棒噠~
各位掰掰~


上一篇
決定欄位! 天下沒有白吃的午餐,股價、籌碼資料的其他來源?
下一篇
python繪製股票K線圖第二彈! 上櫃與上市公司的股價爬蟲
系列文
時空序列分析-關鍵籌碼分析31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言